import json
from typing import Optional

from app.crud import alarm_crud
from app.crud.alarm_crud import safe_slice
from app.schemas.alarm_schemas import (
	AlarmTypeListResponse, RegionAlarmStatsRequest, RegionAlarmStatsResponse, AlarmTypeStatsRequest,
	AlarmTypeStatsResponse
)
from common.base_enums import AlarmCloseReason
from common.database import DatabaseService
from common.exceptions import BizException, ErrorCode
from common.logger import create_logger
from common.redis import get_redis_service
from mqtt.consumer.domain.service import AlarmService
from mqtt.consumer.domain.strategy import ThresholdBackToNormalStrategy, AutoCloseOnRecoveredStrategy

logger = create_logger(__name__)


async def recover_alarm_by_work_order(
		db: DatabaseService,
		alarm_id: int,
		close_reason: AlarmCloseReason,
		operator: str,
		recover_token: Optional[str] = None,
) -> None:
	"""
	人工关单入口：直接调用 domain 服务
	"""
	# FastAPI 侧事务由 db.transaction() 保证
	service = AlarmService(
		db=db,  # DatabaseService 同样实现了 execute_query 等协议
		recovery_strategy=ThresholdBackToNormalStrategy(),  # 人工关单无需恢复策略，可给空实现
		close_strategy=AutoCloseOnRecoveredStrategy(),  # 人工关单也走"恢复即关单"
	)
	# 人工关单视为"已恢复"，复用 domain 逻辑
	with db.transaction():
		closed = service.close_work_order_if_needed(alarm_id)
		if closed is None:
			raise BizException.from_error_code(ErrorCode.NOT_FOUND)


class AlarmStatisticsService:
	"""告警统计服务"""

	# 缓存键前缀
	ALARM_TYPES_CACHE_KEY = "alarm:types:list"
	REGION_ALARM_STATS_CACHE_KEY = "alarm:region:stats:{region_code}:{alarm_type}:{start_time}:{end_time}"
	ALARM_TYPE_STATS_CACHE_KEY = "alarm:type:stats:{region_code}"

	# 缓存过期时间（秒）
	ALARM_TYPES_CACHE_TTL = 30  # 1分钟
	REGION_ALARM_STATS_CACHE_TTL = 30  # 1分钟
	ALARM_TYPE_STATS_CACHE_TTL = 30  # 1分钟

	def __init__(self):
		self.redis = get_redis_service()

	async def get_alarm_types(self, db: DatabaseService) -> AlarmTypeListResponse:
		"""获取告警类型列表

		从缓存或数据库获取告警类型列表

		Args:
			db: 数据库服务

		Returns:
			AlarmTypeListResponse: 告警类型列表响应
		"""
		# 尝试从缓存获取
		cache_key = self.ALARM_TYPES_CACHE_KEY
		cached_data = self.redis.get(cache_key)

		if cached_data:
			try:
				cached_json = json.loads(cached_data)
				return AlarmTypeListResponse(**cached_json)
			except Exception as e:
				logger.error(f"解析告警类型缓存数据失败: {e}")

		# 从数据库获取
		alarm_types = await alarm_crud.get_alarm_types(db)
		response = AlarmTypeListResponse(items=alarm_types)

		# 更新缓存
		try:
			self.redis.set(
				cache_key,
				response.model_dump_json(),
				ex=self.ALARM_TYPES_CACHE_TTL
			)
		except Exception as e:
			logger.error(f"缓存告警类型数据失败: {e}")

		return response

	async def get_region_alarm_stats(
			self,
			db: DatabaseService,
			request: RegionAlarmStatsRequest
	) -> RegionAlarmStatsResponse:
		"""获取区域告警统计

		根据省市区代码查询下级区域的告警统计数据

		Args:
			db: 数据库服务
			request: 请求参数

		Returns:
			RegionAlarmStatsResponse: 区域告警统计响应
		"""
		# 构建缓存键
		alarm_type_str = request.alarm_type or "all"
		start_time_str = request.start_time.strftime("%Y%m%d%H")
		end_time_str = request.end_time.strftime("%Y%m%d%H")
		province_str = request.province_code or "none"
		city_str = request.city_code or "none"
		area_str = request.area_code or "none"

		cache_key = f"alarm:region:stats:{province_str}:{city_str}:{area_str}:{alarm_type_str}:{start_time_str}:{end_time_str}"

		if not request.force_refresh:
			# 从缓存获取
			cached_data = self.redis.get(cache_key)
			if cached_data:
				try:
					return RegionAlarmStatsResponse(**cached_data)
				except Exception as e:
					logger.error(f"解析区域告警统计缓存数据失败: {e}")

		region_code = (
				safe_slice(request.area_code, 6) or
				safe_slice(request.city_code, 4) or
				safe_slice(request.province_code, 2)
		)
		stats_items = await alarm_crud.get_region_alarm_stats(
			db,
			region_code,
			request.alarm_type,
			request.start_time,
			request.end_time
		)
		response = RegionAlarmStatsResponse(items=stats_items)

		# 更新缓存
		try:
			self.redis.set(
				cache_key,
				response.model_dump_json(),
				ex=self.REGION_ALARM_STATS_CACHE_TTL
			)
		except Exception as e:
			logger.error(f"缓存区域告警统计数据失败: {e}")

		return response

	async def get_alarm_type_stats(
			self,
			db: DatabaseService,
			request: AlarmTypeStatsRequest
	) -> AlarmTypeStatsResponse:
		"""获取告警类型统计

		根据区域代码查询各告警类型的数量统计

		Args:
			db: 数据库服务
			request: 请求参数

		Returns:
			AlarmTypeStatsResponse: 告警类型统计响应
		"""

		# 构建缓存键
		alarm_type_str = request.alarm_type or "all"
		cache_key = self.ALARM_TYPE_STATS_CACHE_KEY.format(
			region_code=request.region_code
		)
		if not request.force_refresh:
			# 尝试从缓存获取
			cached_data = self.redis.get(cache_key)
			if cached_data:
				try:
					return AlarmTypeStatsResponse(**cached_data)
				except Exception as e:
					logger.error(f"解析告警类型统计缓存数据失败: {e}")

		# 从数据库获取
		region_code = (
				safe_slice(request.area_code, 6) or
				safe_slice(request.city_code, 4) or
				safe_slice(request.province_code, 2)
		)
		stats_items = await alarm_crud.get_alarm_type_stats(
			db,
			region_code,
			request.station_id,
			request.start_time,
			request.end_time,
			request.limit
		)
		response = AlarmTypeStatsResponse(items=stats_items)

		# 更新缓存
		try:
			self.redis.set(
				cache_key,
				response.model_dump_json(),
				ex=self.ALARM_TYPE_STATS_CACHE_TTL
			)
		except Exception as e:
			logger.error(f"缓存告警类型统计数据失败: {e}")

		return response
